﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MyPublisher.Configuration;
using System.Workflow.Runtime;
using MyPublish.Connection;
using System.Net;
using MyPublish.Connection.Syndication;
using System.IO;
using MyPublisher.Composition;
using MyPublisher.Package;
using System.Workflow.Activities;
using System.Xml.Linq;
using System.Threading;
using System.Configuration;
using System.ServiceModel;
using MyPublisher.Data;

namespace MyPublisher.Service
{
	public class PublishService : IPublishService
	{
        public MyPublisherConfigurationSection Configuration { get; private set; }
        public PublishInfo PublishInfo { get; private set; }
        public string DocumentId { get; set; }

        public PublishService() { }

        public byte[] Download(string documentId)
        {
            using (Storage.Storage storage = CreateStorage())
            {
                string outputpath = BuildOutputPath("", storage, documentId.ToString() + ".zip");
                return File.ReadAllBytes(outputpath);
            }
        }

        public string Publish(PublishInfo info)
        {
            PublishInfo = info;

            BuildEnvironment();
            ConnectBlog();
            Transform();
            Composite();
            Package();
            AddMeta();

            return DocumentId;
        }

        private void BuildEnvironment()
        {
            DocumentId = Guid.NewGuid().ToString();
            try
            {
                Configuration =
                    ConfigurationManager.GetSection("mypublisher.config") as MyPublisherConfigurationSection;
            }
            catch (Exception e)
            {
                throw new ApplicationException("Failed to load configuration.", e);
            }
            using (Storage.Storage storage = CreateStorage())
            {
                string path = DocumentId.ToString();
                foreach (string file in storage.GetFiles(path, "*.*", false))
                {
                    try { storage.DeleteFile(file); }
                    catch { }
                }
            }
        }

        private Storage.Storage CreateStorage()
        {
            StorageConfiguration stoageConfiguration =
                Configuration.StorageManager.DefaultConfiguration;
            Type type = stoageConfiguration.StorageType;
            Storage.Storage storage = Activator.CreateInstance(type) as Storage.Storage;
            storage.Initialize(stoageConfiguration.Name,
                stoageConfiguration.ConnctionString);

            return storage;
        }

        public void ConnectBlog() 
        {
            DataConnectionString connString = new DataConnectionString()
            {
                Provider = PublishInfo.Provider,
                Password = PublishInfo.Password,
                UserName = PublishInfo.UserName,
            };
            WebProxy proxy = Configuration.ConnectionManager.WebProxy;
            using (DataConnection conn = new DataConnection(connString, proxy))
            {
                conn.Open();
                IEnumerable<object> blogs = conn.ExecuteQuery();
                foreach (BlogData blog in blogs)
                {
                    IEnumerable<object> posts = conn.ExecuteQuery(blog);
                    foreach (PostData post in posts)
                    {
                        string title = post.Title.Text;
                        string contents = post.Content.Content;
                        if (contents.Length == 0)
                            continue;

                        using (Storage.Storage storage = CreateStorage())
                        {
                            storage.Open();
                            string fileName = Guid.NewGuid().ToString() + ".htm";
                            string path = DocumentId.ToString() + "/" + fileName;
                            using (Stream stream = storage.CreateFile(path))
                            {
                                using (TextWriter writer = new StreamWriter(stream, Encoding.UTF8))
                                {
                                    writer.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
                                    writer.WriteLine(string.Format("<html><head><title>{0}</title></head><body>",title));
                                    writer.WriteLine(string.Format("<h1>{0}</h1>", title));
                                    writer.Write(contents);
                                    writer.WriteLine("</body></html>");
                                }
                            }
                        }
                    }
                }
            }
        }

        public void Transform()
        {
            Type type = Configuration.TransformManager["DOTNET.Html2WordML"].TranformType;
            ITransform transform = Activator.CreateInstance(type) as ITransform; 
            using (Storage.Storage storage = CreateStorage())
            {
                string path = DocumentId.ToString();
                foreach (string file in storage.GetFiles(path, "*.htm", false))
                {
                    Fragments inputs = new Fragments();
                    inputs.Add(new Fragment(FragmentType.HTML, file));

                    string filename = file.Substring(file.LastIndexOf("\\") + 1);
                    filename = filename.Replace(".htm", ".docx");
                    string outputPath = BuildOutputPath(DocumentId, storage, filename);
                    Fragment output = new Fragment(FragmentType.WordML, outputPath);
                    transform.Tranform(inputs, output);
                }
            }
        }

        public void Composite()
        {
            using (Storage.Storage storage = CreateStorage())
            {
                Fragments inputs = new Fragments();
                string path = DocumentId.ToString();
                foreach (string file in storage.GetFiles(path, "*.docx", false))
                {
                    inputs.Add(new Fragment(FragmentType.XML, file));
                }

                Type type = Configuration.CompositionManager.DefaultProviderType;
                IComposition composition = Activator.CreateInstance(type) as IComposition;
                XDocument document = new XDocument(new XDeclaration("1.0", "utf-8", null));
                string outputPath = BuildOutputPath(DocumentId, storage, "index.docx");
                Fragment output = new Fragment(FragmentType.WordML, outputPath);
                composition.Composite(inputs, output);
            }
        }

        public void Package()
        {
            using (Storage.Storage storage = CreateStorage())
            {
                using (MyPackage package = MyPackage.CreatePackage())
                {
                    foreach (string file in storage.GetFiles(DocumentId.ToString(), "*.htm", false))
                    {
                        ContentPart part = new ContentPart()
                        {
                            Link = file,
                            MediaType = "application/htm",
                            Path = file,
                            Title = new FileInfo(file).Name
                        };
                        package.AddPart(part);
                    }

                    foreach (string file in storage.GetFiles(DocumentId.ToString(), "*.docx", false))
                    {
                        PublicationPart part = new PublicationPart()
                        {
                            MediaType = "application/pdf",
                            Path = file,
                            Title = new FileInfo(file).Name
                        };
                        package.AddPart(part);
                    }
                    string outputpath = BuildOutputPath("", storage, DocumentId.ToString() + ".zip");
                    package.Save(outputpath);
                }
                string dir = BuildOutputPath(DocumentId, storage, "");
                try
                {
                    Directory.Delete(dir, true);
                }
                catch { }
            }
        }

        private void AddMeta()
        {
            Publication pub = new Publication()
            {
                PublicationId = new Guid(DocumentId),
                Description = "",
                UserName = PublishInfo.UserName,
                Provider = PublishInfo.Provider,
                UserId = Guid.NewGuid()
            };
            pub.AddSubmit();
        }

        private string BuildOutputPath(string instanceid,Storage.Storage storage,string filename)
        {
            if (!string.IsNullOrEmpty(instanceid))
            {
                string path = instanceid.ToString() + "/" + filename;
                storage.ResolvePath(ref path);
                return path;
            }
            else
            {
                string path = filename;
                storage.ResolvePath(ref path);
                return path;
            }
        }
    }
}
